<script>
import { h, inject, ref, provide, onMounted, nextTick, watch, onBeforeUpdate } from 'vue';
import { InitMapping } from '../util/useParams'
import { FormatViewTypeString, StringToFun } from '../util/index'


export default {
    props: ['Config', 'Value'],
    emits: ['onEvent'],
    setup(props, { expose }) {
        // 模板获取
        const TemplateMapping = inject('TemplateMapping');
        // 模板元素
        const ObjectItem = TemplateMapping.Base.FormItem

        const Model = JSON.parse(JSON.stringify(props.Config));
        const _Obj = InitMapping(Model.param);

        // 初始化对象绑定数据
        let ParamMapping = _Obj.ParamMapping;
        let Value = _Obj.Value;
        let RenderOption = ref(Model.view);
        let WacthEventMapping = _Obj.WacthEventMapping;
        let DictMapping = _Obj.DictMapping;
        let ChangeOptionMapping = _Obj.ChangeOptionMapping;
        let RuleMapping = _Obj.RuleMapping;


        // 处理显示隐藏
        const isEnable = option => {
            if (option.Event && option.Event.Enable && Value.value) {
                return !!StringToFun(option.Event.Enable, Value.value);
            } else {
                return true
            }
        };



        // 前端模板缓存
        let itemRefs = {}
        // 缓存 Error Message
        const ErrorMessage = ref({});
        // 缓存子元素实例
        const setItemRef = (el, key) => {
            itemRefs[key] = el;
        }
        // 错误验证执行方法
        // 目前只有必填项、参数定义项、组件定义项 的报错信息
        const CheckErrorMessage = function (val) {
            nextTick(() => {
                for (const key in ParamMapping.value) {
                    const paramItem = ParamMapping.value[key];
                    if (val) {
                        const RuleItem = RuleMapping[key];
                        var Enable = isEnable(paramItem);
                        if (paramItem.required && (val[key] === undefined || val[key] === null || val[key] === '')) {
                            // 判断值是否为空（必填项）
                            ErrorMessage.value[key] = `${paramItem.label}为必填项`;
                            itemRefs[key] && itemRefs[key].SetError(ErrorMessage.value[key]);
                        } else if (Enable && RuleItem) {
                            // 值得正则验证（参数定义项）
                            var ans = new RegExp(RuleItem.RegExp).test(val[key]);
                            ErrorMessage.value[key] = ans ? null : RuleItem.Error;
                            itemRefs[key] && itemRefs[key].SetError(ErrorMessage.value[key]);
                        } else {
                            // 判断报错信息是否来自于组件内部，如果不是，则删除报错信息
                            if (ErrorMessage.value[key] == `${paramItem.label}为必填项` || (RuleItem && ErrorMessage.value[key] == RuleItem.Error)) {
                                ErrorMessage.value[key] = null;
                                itemRefs[key] && itemRefs[key].SetError(null);
                            }
                        }
                    }
                };
            })
        };
        // 模板更新时触发错误消息验证
        onBeforeUpdate(() => {
            itemRefs = {};
            CheckErrorMessage(Value.value);
        })

        // 监听错误验证信息
        watch(Value, val => {
            CheckErrorMessage(val);
        }, { deep: true, immediate: true })


        // 子组件实例化对象缓存
        const SelectEvevtMapping = {};
        provide('SetSelectItemMapping', function (key, Event) {
            SelectEvevtMapping[key] = Event;
        })

        // 处理 Options 改变事件
        const SetOptions = function (val) {
            for (const key in ChangeOptionMapping) {
                const EventMapping = ChangeOptionMapping[key];
                for (const Itemkey in EventMapping) {
                    const element = EventMapping[Itemkey];
                    if (!!StringToFun(Itemkey, val)) {
                        var _ItemOption = ParamMapping.value[key];
                        for (const key in element) {
                            const item = element[key];
                            _ItemOption[key] = item
                        };
                    }
                }
            };
        }

        // 监听值信息，当值任何值发生变化时，判断监听事件与联动事件
        watch(Value, val => {
            for (const key in WacthEventMapping) {
                const EventMapping = WacthEventMapping[key];
                var _val = StringToFun(EventMapping, val);
                if (Object.prototype.toString.call(_val) == '[object Array]') {
                    val[key].length = 0;
                    for (let i = 0; i < _val.length; i++) {
                        const element = _val[i];
                        val[key].push(element);
                    }
                } else {
                    val[key] = _val;
                }
            };
            SetOptions(val);
        }, { deep: true, immediate: true });



        // 联动更新字典项信息
        const UpdateSelectDict = function (key) {
            if (DictMapping[key] && SelectEvevtMapping[key]) {
                // 所有需要联动的字典项代码中，都需要包含取值（取到所选值得对象）的功能
                const Dict = SelectEvevtMapping[key].exposed.GetValueObject();
                for (const ToKey in DictMapping[key]) {
                    const ToObj = DictMapping[key][ToKey];
                    if (SelectEvevtMapping[ToKey]) {
                        var _value = null;
                        if (ToObj.default != 'null' && Dict[ToObj.key] && Dict[ToObj.key][ToObj.default]) {
                            _value = Dict[ToObj.key][ToObj.Default];
                        }
                        SelectEvevtMapping[ToKey].exposed.SetDictValue(Dict[ToObj.key], _value);
                    }
                }
            }
        }
        // 给 Select 添加数据切换方法
        provide('UpdateSelectDict', UpdateSelectDict);

        // 初始化数据中的表单联动
        onMounted(() => {
            nextTick(() => {
                for (const key in DictMapping) {
                    UpdateSelectDict(key);
                }
            })
        })


        // 获取模板方法
        const GetTemplate = (str, Temp) => {
            return new Function('Temp', 'return Temp.' + str)(Temp);
        };

        // 渲染 Object 元素
        function RenderObjectItem(Type, FormConfig) {
            return h(ObjectItem, {
                option: FormConfig,
                ViewType: Type.ViewType,
                Level: props.Level || 0,
                Value: Value.value,
                ref: (el) => {
                    // 为渲染元素绑定缓存
                    el && setItemRef(el, FormConfig.key);
                },
                onSetError: (val) => {
                    // 显示组件内部来源的错误信息
                    ErrorMessage.value[FormConfig.key] = val;
                },
                onUpdateValue: (key, val) => {
                    // 值绑定事件（抛弃了默认的双向绑定，为组件可自定义绑定多个值做扩展）
                    Value.value[key] = val
                }
            });
        }

        // 递归生成页面元素
        function RenderLayout(options) {
            if (!options) return h('div', '表单配置获取失败')
            var Obj = {};
            // 根据表单项，生成子元素
            for (const key in options.slots) {
                const item = options.slots[key];
                Obj[key] = (slotProps) => {
                    const childElement = [];
                    for (let i = 0; i < item.length; i++) {
                        const element = item[i];
                        let _item = null;
                        if (typeof element == 'string') {
                            // 默认，基本规则设定子组件为字符串
                            // 根据key值来渲染数值元素
                            var _ItemOptions = FormatViewTypeString(element);
                            var ItemOptions = ParamMapping.value[_ItemOptions.Key];
                            // 根据执行信息，判断组件是否被隐藏，如果隐藏，跳过渲染
                            if (isEnable(ItemOptions)) {
                                _item = RenderObjectItem(_ItemOptions, ItemOptions)
                                slotProps.onHideItem && slotProps.onHideItem(slotProps.keyStr, true);
                            } else {
                                // 告诉显然的父级组件，是否显示改对象 (RowGroup 项使用)
                                slotProps.onHideItem && slotProps.onHideItem(slotProps.keyStr, false);
                            }
                        } else {
                            // 递归渲染，
                            // 判断如果不是字符串，走模板渲染，做模板嵌套功能
                            _item = RenderLayout(element);
                        }
                        // 判断没有值进行了渲染，跳过渲染步骤
                        _item && childElement.push(_item);
                    }
                    return childElement
                }
            };
            var _props = {
                option: options.options,
                Value: Value.value,
                onUpdateValue: (key, val) => {
                    // 给模板组件一个修改 Value 值的功能
                    Value.value[key] = val
                }
            }

            return h(GetTemplate(options.type, TemplateMapping), _props, Obj)
        }


        // 取值
        const GetValue = (callback) => {
            let ErrMessage = {};
            let HaveError = null;
            for (const key in ErrorMessage.value) {
                const element = ErrorMessage.value[key];
                if (element) {
                    ErrMessage[key] = {
                        Param: JSON.parse(JSON.stringify(ParamMapping.value[key])),
                        Str: element,
                    };
                    HaveError = true;
                }
            }
            return callback(HaveError && ErrMessage, Value.value);
        };
        // 赋值
        const SetValue = val => {
            var _Obj = { ...Value.value, ...val };
            Value.value = _Obj;
        };

        const ReadonlyAll = function (val) {
            for (const key in ParamMapping.value) {
                const element = ParamMapping.value[key];
                element.readonly = !!val;
            }
        };

        // 输出
        expose({
            ParamMapping,
            GetValue,
            SetValue,
            ReadonlyAll,
        });

        // 渲染方法
        return () => RenderLayout(RenderOption.value);
    }
}
</script>